home *** CD-ROM | disk | FTP | other *** search
/ SPACE 1 / SPACE - Library 1 - Volume 1.iso / misc~1 / 199 / src / mk.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-03-14  |  13.3 KB  |  641 lines

  1. /* 
  2.  * simple make program 
  3.  *
  4.  *    make [options] [targets]
  5.  *
  6.  *    options are
  7.  *        -i        ignore non-zero program returns
  8.  *        -f file        makefile name (default is "makefile")
  9.  *        -n        don't execute any commands
  10.  *        -t        just touch the targets, don't build 
  11.  *        -r        ignore built-in rules
  12.  *        -d        debug flag (noisy output)
  13.  *        -h        hold the screen
  14.  *
  15.  * this program reads a makefile to build the named targets
  16.  * a makefile contains rules, macro definitions, or dependencies
  17.  * an example makefile might be:
  18.  *
  19.  *     # this is a sample makefile for a spreadsheet
  20.  *     OBJ = main.s io.s calc.s
  21.  *     ss: $(OBJ) ss.h
  22.  *        d:as.ttp d:ttp.s $(OBJ) -L d:lib.a
  23.  *
  24.  * there are built-in rules to convert .c to .s, .c to .ttp, and .c to .prg
  25.  */
  26.  
  27. #include <stdio.h>
  28.  
  29. #define MAXLINE    256
  30. #define MAXSYM    300
  31. #define MAXRULE    100
  32. #define MAXSPC    20000
  33.  
  34. #define TIME    0x2C
  35. #define DATE    0x2A
  36. #define EXEC    0x4B
  37. #define RENAME    0x56
  38. #define GSDTOF    0x57
  39.  
  40. typedef struct x0 {        /* structure for command lists        */
  41.     char *cmd;        /*    pointer to the command line    */
  42.     struct x0 *next;    /*    pointer to the next one        */
  43. } Command;
  44.  
  45. typedef struct x1 {        /* structure for dependencies        */
  46.     int    dep;        /*    name of a parent        */
  47.     struct x1 *next;    /*    pointer to next parent        */
  48. } Depend;
  49.  
  50. typedef struct {        /* structure for rules            */
  51.     int     from;        /*    the extension of the parent    */
  52.     int    to;        /*    the extension of the child    */
  53.     Command    *cmd;        /*     the commands to make the child    */
  54. } Rule;
  55.  
  56. FILE    *in;            /* makefile input file handle        */
  57. int    eof;            /* eof flag for makefile input stream    */
  58. int    no_exec = 0;        /* don't execute the commands        */
  59. int    debug = 0;        /* debug output flag            */
  60. int    ignore = 0;        /* ignore non-zero returns        */
  61. int    norules = 0;        /* no built-in rules            */
  62. int    hold = 0;        /* hold the screen (for GEM users    */
  63. char     line[MAXLINE];        /* buffer containing current input line    */
  64. char    extra[MAXLINE];        /* extra buffer for macro expansion    */
  65. char    name[MAXLINE];        /* space for making up names        */
  66. char     *macro[MAXSYM];        /* pointer to the macros        */
  67. char     *sym[MAXSYM];        /* pointer to the symbol names        */
  68. Depend    *depend[MAXSYM];    /* pointer to dependencies        */
  69. Command    *command[MAXSYM];    /* index into commands            */
  70. Rule    rules[MAXRULE];        /* rules                */
  71. int    lrule = 0;        /* last rule used            */
  72. char    space[MAXSPC];        /* free space (poor person's alloc)    */
  73. int    lspc = 0;        /* last entry in space            */
  74. int    firstsym = -1;        /* what's first dependency declared    */
  75. char    base[MAXLINE];        /* buffer for '*' macro            */
  76. char    target[MAXLINE];    /* buffer for '@' macro            */
  77.  
  78. char     *save();        /* save a string            */
  79. char    *alloc();        /* allocate space            */
  80. Command    *cmdlist();        /* make up a command list        */
  81. Command *add_cmd();        /* add a command to a command list    */
  82. long    date();            /* get the date of a file        */
  83.  
  84. main(argc, argv) char *argv[]; {
  85.     int i, err, tch;
  86.     char *name;
  87.     i = 1;
  88.     name = "makefile";
  89.     tch = err = 0;
  90.     for (i = 1; i < argc && *argv[i] == '-'; i++) {
  91.         switch (argv[i][1]) {
  92.         case 'f': case 'F':
  93.             i++;
  94.             name = argv[i];
  95.             break;
  96.         case 'i': case 'I':
  97.             ignore = 1;
  98.             break;
  99.         case 'd': case 'D':
  100.             debug = 1;
  101.             break;
  102.         case 't': case 'T':
  103.             tch = 1;
  104.             break;
  105.         case 'n': case 'N':
  106.             no_exec = 1;
  107.             break;
  108.         case 'r': case 'R':
  109.             norules = 1;
  110.             break;
  111.         case 'h': case 'H':
  112.             hold = 1;
  113.             break;
  114.         default:
  115.             printf("unknown option: %s\n", argv[i]);
  116.             err = 1;
  117.             break;
  118.         }
  119.     }
  120.     if (err) error("usage: make [-i -n -t -r -d -h -f file] [targets]\n");
  121.     if (tch) {
  122.         while (i < argc)
  123.             touch(argv[i++]);
  124.     }
  125.     else    {
  126.         init();
  127.         input(name);
  128.         if (i == argc && firstsym >= 0)
  129.             make(firstsym);
  130.         else while (i < argc)
  131.             make(lookup(argv[i++]));
  132.     }
  133.     bye(0);
  134. }
  135.  
  136. /* initialize the symbol table and add built-in rules */
  137.  
  138. init() {
  139.     int i;
  140.     Command *cp;
  141.     rules[0].cmd = NULL;
  142.     for (i = 0; i < MAXSYM; i++)
  143.         sym[i] = NULL;
  144.     if (norules) return;
  145.     /* built-in rules */
  146.     cp = NULL;
  147.     cp = add_cmd(cp, " d:cc.ttp $*.c");
  148.     cp = add_cmd(cp, " d:as.ttp -o $*.ttp d:ttp.s yc.out -L d:lib.a");
  149.     cp = add_cmd(cp, " rm yc.out");
  150.     add_rule(".c", ".ttp", cp);
  151.     cp = NULL;
  152.     cp = add_cmd(cp, " d:cc.ttp $*.c");
  153.     cp = add_cmd(cp, " d:as.ttp -o $*.prg d:prg.s yc.out -L d:gem.a d:lib.a");
  154.     cp = add_cmd(cp, " rm yc.out");
  155.     add_rule(".c", ".prg", cp);
  156.     cp = NULL;
  157.     cp = add_cmd(cp, " d:cc.ttp -o $*.s $*.c");
  158.     add_rule(".c", ".s", cp);
  159. }
  160.  
  161. /* read in and parse the makefile */
  162.  
  163. input(filename) char *filename; {
  164.     if (!(in = fopen(filename, "r")))
  165.         return;
  166.     getline();
  167.     while (!eof) {
  168.         if (*line == '#')
  169.             getline();
  170.         else if (macdef())
  171.             getline();
  172.         else if (*line == '.')
  173.             rule();
  174.         else if (*line > ' ')
  175.             dependency();
  176.         else    getline();
  177.     }
  178.     fclose(in);
  179. }
  180.  
  181. /* read in a line from the makefile */
  182.  
  183. getline() {
  184.     int c;
  185.     char *l;
  186.     l = line;
  187.     while ((c = getc(in)) != EOF && c != '\n') {
  188.         if (c == '\\') {
  189.             c = getc(in);
  190.             if (c != '\n' && c != '\r')
  191.                 *l++ = '\\';
  192.         }
  193.         if (c != '\r') 
  194.             *l++ = c;
  195.     }
  196.     *l = 0;
  197.     eof = (c == EOF);
  198. }
  199.  
  200. /* expand all the macro's in the current line */
  201.  
  202. expand() { 
  203.     int done;
  204.     char *e, *l, *m;
  205.     while (1) {
  206.         e = extra;
  207.         l = line;
  208.         done = 1;
  209.         while (*e++ = *l++)
  210.             if (*l == '$') 
  211.                 done = 0;
  212.         if (done) 
  213.             break;
  214.         e = extra;
  215.         l = line;
  216.         while (*e) {
  217.             if (*e == '$') {
  218.                 e++;
  219.                 m = name;
  220.                 if (*e == '(') { /* multi letter macro */
  221.                     e++;
  222.                     while (*e != ')') 
  223.                         *m++ = *e++;
  224.                     e++;
  225.                 }
  226.                 else    *m++ = *e++; /* one letter macro */
  227.                 *m = 0;
  228.                 if (m = macro[lookup(name)]) {
  229.                     while (*m) 
  230.                         *l++ = *m++;
  231.                 }
  232.                 else    error("$(%s) not defined", name);
  233.             }
  234.             else    *l++ = *e++;
  235.         }
  236.         *l = 0;
  237.     }
  238. }
  239.  
  240. /* read in a rule, ".from.to:" followed by command lines */
  241.  
  242. rule() {
  243.     int i, j;
  244.     char to[10], from[10], *l;
  245.     l = line;
  246.     i = j = 0;
  247.     if (*l != '.') 
  248.         error("bad rule entry");
  249.     do { from[j++] = *l++; } while (*l && *l != '.');
  250.     from[j] = 0;
  251.     if (*l != '.') 
  252.         error("bad rule entry");
  253.     j = 0;
  254.     do { to[j++] = *l++; } while (*l && *l != ':');
  255.     to[j] = 0;
  256.     add_rule(from, to, cmdlist());
  257. }
  258.  
  259. /* build a list of commands, return pointer to them */
  260.  
  261. Command *
  262. cmdlist() {
  263.     Command *r;
  264.     r = NULL;
  265.     getline();
  266.     while (*line && *line <= ' ') {
  267.         r = add_cmd(r, save(line));
  268.         getline();
  269.     }
  270.     return r;
  271. }
  272.  
  273. /* check for a macro def and process if there */
  274.  
  275. macdef() { 
  276.     char *l, *b, *e;
  277.     l = line;
  278.     while (*l && *l <= ' ')    l++;
  279.     b = l;
  280.     while (*l && *l > ' ')     l++;
  281.     e = l;
  282.     while (*l && *l <= ' ') l++;
  283.     if (*l++ == '=') { /* got a macro */
  284.         *e = 0;
  285.         add_mac(b, save(l));
  286.         return 1;
  287.     }
  288.     else    return 0;
  289. }
  290.  
  291. /* parse a dependency, "child [children]*: [parents]*" */
  292.  
  293. dependency() {
  294.     int i, j, target[20], depend[20];
  295.     Command *cmd;
  296.     expand();
  297.     i = namelist(target, 0);
  298.     if (line[i] != ':')
  299.         error("bad dependency rule");
  300.     i = namelist(depend, i+1);
  301.     cmd = cmdlist();
  302.     for (i = 0; target[i]; i++) {
  303.         for (j = 0; depend[j]; j++)
  304.             add_dep(target[i], depend[j]);
  305.         set_cmd(target[i], cmd);
  306.     }
  307.     if (firstsym < 0) 
  308.         firstsym = target[0];
  309. }
  310.  
  311. /* gather up a list of names in the input line */
  312.  
  313. namelist(list, i) int *list; {
  314.     int t, j;
  315.     t = 0;
  316.     while (line[i] && line[i] != ':') {
  317.         while (line[i] && line[i] <= ' ') 
  318.             i++;
  319.         j = 0;
  320.         if (line[i]) {
  321.             while (line[i] && line[i] > ' ' && line[i] != ':')
  322.                 name[j++] = line[i++];
  323.             name[j] = 0;
  324.             list[t++] = lookup(name);
  325.         }
  326.     }
  327.     list[t] = 0;
  328.     return i;
  329. }
  330.  
  331. /* add a new command to the end of a command list */
  332.  
  333. Command *
  334. add_cmd(cp, cmd) Command *cp; char *cmd; {
  335.     Command *p, *r;
  336.     p = alloc((short)sizeof(Command));
  337.     p->cmd = cmd;
  338.     p->next = NULL;
  339.     if (cp == NULL)
  340.         r = p;
  341.     else     {
  342.         r = cp;
  343.         while (cp->next)
  344.             cp = cp->next;
  345.         cp->next = p;
  346.     }
  347.     return r;
  348. }
  349.  
  350. /* add a parent (target) to a child (dependency) */
  351.  
  352. add_dep(target, dep) {
  353.     Depend *p;
  354.     if (debug) printf("add_dep(%s,%s)\n", sym[target], sym[dep]);
  355.     p = alloc((short)sizeof(Depend));
  356.     p->dep = dep;
  357.     p->next = depend[target];
  358.     depend[target] = p;
  359. }
  360.  
  361. /* add a macro */
  362.  
  363. add_mac(name, str) char *name, *str; {
  364.     if (debug) printf("add_mac(%s,%s)\n", name, str);
  365.     macro[lookup(name)] = str;
  366. }
  367.  
  368. /* add a command list to a target */
  369.  
  370. set_cmd(target, cp) Command *cp; {
  371.     if (debug) printf("set_cmd(%s,%lx)\n", sym[target], cp);
  372.     command[target] = cp;
  373. }
  374.  
  375. /* add a rule */
  376.  
  377. add_rule(from, to, cp) char *from, *to; Command *cp; {
  378.     if (debug) printf("add_rule(%s,%s,%lx)\n", from, to, cp);
  379.     rules[lrule].from = lookup(from);
  380.     rules[lrule].to = lookup(to);
  381.     rules[lrule].cmd = cp;
  382.     if (++lrule >= MAXRULE)
  383.         error("too many rules");
  384.     rules[lrule].cmd = NULL;
  385. }
  386.  
  387. /* build a child by first building the parents */
  388.  
  389. make(child) {
  390.     int mkflag;
  391.     long chdate;
  392.     Command *cp;
  393.     Depend *dp;
  394.  
  395.     if (debug) printf("make(%s)\n", sym[child]);
  396.     chdate = date(sym[child]);
  397.     mkflag = 0;
  398.  
  399.     if (dp = depend[child]) {
  400.         while (dp) {
  401.             make(dp->dep);
  402.             if (check(dp->dep, chdate))
  403.                 mkflag = 1;
  404.             dp = dp->next;
  405.         }
  406.     }
  407.     else    mkflag = 1;
  408.  
  409.     if (mkflag) {
  410.         strcpy(target, sym[child]);
  411.         add_mac("@", target);
  412.         if (cp = command[child]) {
  413.             rm(target);
  414.             execute(cp);
  415.         }
  416.         else    chkrule(child, chdate);
  417.     }
  418. }
  419.  
  420. /* set the date/time of the named file to the present */
  421.  
  422. touch(name) char *name; {
  423.     int fd, r;
  424.     long dt;
  425.     if ((fd = open(name, 2)) >= 0) {
  426.         dt = (trap(1, DATE) && 0xFFFF) | (trap(1, TIME) << 16);
  427.         if (r = trap(1, GSDTOF, &dt, fd, 1))
  428.             error("cannot touch %s (%d)\n", name, r);
  429.         close(fd);
  430.     }
  431.     else    error("cannot open %s (%d)\n", name, fd);
  432. }
  433.  
  434. /* get the date/time of the named file */
  435.  
  436. long
  437. date(name) char *name; {
  438.     int fd;
  439.     unsigned long dt;
  440.     if ((fd = open(name, 0)) >= 0) {
  441.         trap(1, GSDTOF, &dt, fd, 0);
  442.         dt = ((dt >> 16) & 0xFFFFL) | (dt << 16); /* swap words */
  443.         close(fd);
  444.     }
  445.     else    dt = 0L;
  446.     if (debug) printf("date of %s is %lx\n", name, dt);
  447.     return dt;
  448. }
  449.  
  450. /* compare the date/time of the named file against the child date */
  451.  
  452. check(parent, chdt) long chdt; {
  453.     long pardt;
  454.     pardt = date(sym[parent]);
  455.     return (pardt > chdt);
  456. }
  457.  
  458. /* see if there are any rules that we can use to build the child */
  459.  
  460. chkrule(child, chdate) long chdate; {
  461.     Rule *r;
  462.     char *s, *e;
  463.     int i, j, ext, parent, parbase;
  464.     s = sym[child];
  465.     for (j = i = 0; base[i] = s[i]; i++)
  466.         if (s[i] == '.') 
  467.             j = i;
  468.     if (j == 0) 
  469.         return 0;
  470.     e = &s[j];
  471.     base[j] = 0;
  472.     parbase = lookup(base);
  473.     ext = lookup(e);
  474.     for (i = lrule; --i >= 0; ) {
  475.         r = &rules[i];
  476.         if (r->to == ext) {
  477.             strcpy(extra, base);
  478.             strcat(extra, sym[r->from]);
  479.             parent = lookup(extra);
  480.             make(parent);
  481.             add_mac("*", sym[parbase]); 
  482.             if (check(parent, chdate)) {
  483.                 rm(sym[child]);
  484.                 execute(r->cmd);
  485.                 return;
  486.             }
  487.         }
  488.     }
  489. }
  490.  
  491. /* execute a list of commands */
  492.  
  493. execute(cp) Command *cp; {
  494.     int r;
  495.     while (cp) {
  496.         strcpy(line, cp->cmd);
  497.         expand();
  498.         printf("  %s\n", line);
  499.         if (!no_exec && (r = system(line)) && !ignore)
  500.             error("command failed (%d)", r);
  501.         cp = cp->next;
  502.     }
  503. }
  504.  
  505. /* do something, either built-in (rm, mv) or exec a program */
  506.  
  507. system(s) char *s; {
  508.     int r;
  509.     char *first, *sec;
  510.     while (*s && *s <= ' ') s++;
  511.     first = s;
  512.     while (*s && *s > ' ') s++;
  513.     if (*s) *s++ = 0;
  514.     if (strcmp(first, "rm") == 0) {
  515.         r = rm(s);
  516.     }
  517.     else if (strcmp(first, "mv") == 0) {
  518.         r = mvcp(1, s);
  519.     }
  520.     else if (strcmp(first, "cp") == 0) {
  521.         r = mvcp(0, s);
  522.     }
  523.     else    {
  524.         r = strlen(s--);
  525.         *s = r;
  526.         r = trap(1, EXEC, 0, first, s, "");
  527.     }
  528.     return r;
  529. }
  530.  
  531. /* remove a file */
  532.  
  533. rm(name) char *name; {
  534.     int r;
  535.     r = unlink(name);
  536.     if (r == -33) r = 0; /* name not found same as deleted */
  537.     return r;
  538. }
  539.  
  540. /* move or copy a src file to a destination file */
  541.  
  542. mvcp(mv, s) char *s; {
  543.     int r, c;
  544.     char *src, *dst;
  545.     FILE *in, *out;
  546.     src = strtok(s, " ");
  547.     dst = strtok(NULL, " ");
  548.     if (src == NULL || dst == NULL || *src == 0 || *dst == 0) 
  549.         return 1;
  550.     if (mv) {
  551.         unlink(dst);
  552.         if (r = trap(1, RENAME, 0, src, dst))
  553.             error("can't move %s to %s (%d)\n", src, dst, r);
  554.     }
  555.     else    {        
  556.         if (in = fopen(src, "r")) {
  557.             if (out = fopen(dst, "w")) {
  558.                 while ((c = getc(in)) != EOF)
  559.                     putc(c, out);
  560.                 fclose(out);
  561.             }
  562.             else    error("can't create %s\n", dst);
  563.             fclose(in);
  564.         }
  565.         else    error("can't read %s\n", src);
  566.     }
  567.     return 0;
  568. }
  569.  
  570. /* symbol table lookup */
  571.  
  572. lookup(s) char *s; {
  573.     int i, start;
  574.     upper(s); /* too bad TOS filenames are always uppercase */
  575.     start = i = *s;
  576.     while (sym[i]) {
  577.         if (strcmp(s, sym[i]) == 0)
  578.             return i;
  579.         if (++i >= MAXSYM) 
  580.             i = 0;
  581.         if (i == start)
  582.             error("too many symbols");
  583.     }
  584.     sym[i] = save(s);
  585.     command[i] = macro[i] = depend[i] = 0L;
  586.     return i;
  587. }
  588.  
  589. /* convert a string to all uppercase in place */
  590.  
  591. upper(s) char *s; {
  592.     register int c;
  593.     for ( ; c = *s; s++) {
  594.         if (c >= 'a' && c <= 'z')
  595.             *s = c - 'a' + 'A';
  596.     }
  597. }
  598.  
  599. /* save a string */
  600.  
  601. char *
  602. save(s) char *s; {
  603.     char *r;
  604.     r = &space[lspc];
  605.     while (space[lspc++] = *s++)
  606.         if (lspc >= MAXSPC)
  607.             error("out of free space");
  608.     return r;
  609. }
  610.  
  611. /* allocate some space */
  612.  
  613. char *
  614. alloc(n) {
  615.     char *r;
  616.     if (lspc & 1) 
  617.         lspc++;
  618.     r = &space[lspc];
  619.     lspc = lspc + n;
  620.     if (lspc >= MAXSPC)
  621.         error("out of free space");
  622.     return r;
  623. }
  624.  
  625. bye(n) {
  626.     if (hold) {
  627.         printf("(press any char)\n");
  628.         getchar();
  629.     }
  630.     exit(n);
  631. }
  632.  
  633. /* complain and get out */
  634.  
  635. error(s, a, b, c, d) {
  636.     printf("** ");
  637.     printf(s, a, b, c, d);
  638.     printf("\n");
  639.     bye(1);
  640. }
  641.